package com.onion.orm.plugin.spring;

import com.google.common.collect.Lists;
import com.onion.utils.utils.ClazzUtils;
import com.onion.core.entity.Enhanced;
import com.onion.orm.config.EnhancedConfig;
import com.onion.orm.dao.DaoBuilder;
import com.onion.core.entity.EnhancedType;
import com.onion.orm.support.builder.BeanWrapper;
import com.onion.orm.support.builder.BuildContext;
import com.onion.orm.support.builder.EnhancedDaoBuilder;
import io.github.lukehutch.fastclasspathscanner.scanner.ClassInfo;
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Enhanced Dao注册中心
 * 扫描Enhanced实体，生成Enhanced Dao，并注册至Spring Context进行统一管理
 * @author Asen
 */
public class EnhancedDaoFactory implements BeanDefinitionRegistryPostProcessor {

    private Logger logger = LoggerFactory.getLogger(EnhancedDaoFactory.class);

    private final static AtomicInteger atomicBeanId = new AtomicInteger();
    private EnhancedConfig enhancedConfig;
    private EnhancedScanner enhancedScanner;
    private Map<EnhancedType, DaoBuilder> daoBuilderMap = new HashMap<>();

    public EnhancedDaoFactory(){
        this.initEnhancedConfig();
        this.initEnhancedScanner(enhancedConfig);
        this.initEnhancedDaoBuilder();
    }

    public EnhancedDaoFactory(EnhancedConfig enhancedConfig){
        this.enhancedConfig = enhancedConfig;
        this.initEnhancedScanner(enhancedConfig);
        this.initEnhancedDaoBuilder();
    }

    /**
     * 初始化EnhancedConfig
     */
    private void initEnhancedConfig(){
        enhancedConfig = new EnhancedConfig();
        List<String> scanPath = Lists.newArrayList();
        scanPath.add("com.onion");
        enhancedConfig.setScanPath(scanPath);
    }

    /**
     * 初始化EnhancedScanner
     * @param config
     */
    private void initEnhancedScanner(EnhancedConfig config){
        enhancedScanner = new EnhancedScanner(config);
    }

    /**
     * 初始化动态EnhancedDao构建器
     */
    private void initEnhancedDaoBuilder(){
        daoBuilderMap.put(EnhancedType.ENHANCED, new EnhancedDaoBuilder());
        ServiceLoader<DaoBuilder> daoBuilderSpiLoaders = ServiceLoader.load(DaoBuilder.class);
        if(daoBuilderSpiLoaders != null) {
            Iterator<DaoBuilder> itor = daoBuilderSpiLoaders.iterator();
            while(itor.hasNext()) {
                DaoBuilder builder = itor.next();
                if(daoBuilderMap.containsKey(builder.getType())){
                    logger.warn("DaoBuilder将被覆盖, EnhancedType={}, 替换后为:{}", builder.getType(), builder);
                }
                daoBuilderMap.put(builder.getType(), builder);
            }
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        logger.info("开始初始化 Enhanced dao, scanning on: {}", enhancedConfig.getScanPath());
        ScanResult scanResult = enhancedScanner.doScan();
        List<String> entityList = scanResult.getNamesOfClassesWithAnnotation(Enhanced.class);
        Map<String, ClassInfo> entityMap = scanResult.getClassNameToClassInfo();
        for (String clazzName : entityList) {
            ClassInfo entityInfo = entityMap.get(clazzName);
            Class<?> clazz = ClazzUtils.getClazz(clazzName);
            //获取EnhancedType
            EnhancedType enhanceType = this.getEnhancedType(clazz);
            //构建动态EnhancedDao
            BeanWrapper daoWarapper = this.buildDaoWrapper(enhanceType, new BuildContext(clazz, entityInfo, enhancedConfig));
            //注册Dao Bean至Spring
            registry.registerBeanDefinition(daoWarapper.getName(), daoWarapper.getBean());
        }
        logger.info("完成初始化 Enhanced dao, scanning on: {}", enhancedConfig.getScanPath());
    }

    /**
     * 获取Dao构造器
     * @return
     */
    private BeanWrapper buildDaoWrapper(EnhancedType enhancedType, BuildContext buildContext){
        DaoBuilder daoBuilder = daoBuilderMap.get(enhancedType);
        if(daoBuilder != null){
            return daoBuilder.build(buildContext);
        }else{
            throw new RuntimeException("系统启动错误，未找到DaoBuilder:" + enhancedType);
        }
    }

    /**
     * 获取EnhancedType
     * @param clazz
     * @return
     */
    private EnhancedType getEnhancedType(Class<?> clazz) {
        Enhanced enhanced = clazz.getAnnotation(Enhanced.class);
        if (enhanced != null && enhanced.type() != null) {
            return enhanced.type();
        } else {
            return EnhancedType.ENHANCED;
        }
    }

}